package NB;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map.Entry;

import track2.context.Config;

public class IdnbTrain {
	private static HashMap<Integer, Double> titleC;
	private static HashMap<Integer, Double> titleNC;
	private static HashMap<Integer, Double> queryC;
	private static HashMap<Integer, Double> queryNC;
	private static HashMap<Integer, Double> descriptionC;
	private static HashMap<Integer, Double> descriptionNC;
	private static HashMap<Integer, Double> purchaseC;
	private static HashMap<Integer, Double> purchaseNC;
	private static HashMap<Integer, Double> userC;
	private static HashMap<Integer, Double> userNC;
	private static HashMap<Integer, Double> adC;
	private static HashMap<Integer, Double> adNC;
	private static HashMap<Integer, Double> advertiserC;
	private static HashMap<Integer, Double> advertiserNC;
	private static HashMap<Integer, Double> depthC;
	private static HashMap<Integer, Double> depthNC;
	private static HashMap<Integer, Double> positionC;
	private static HashMap<Integer, Double> positionNC;

	private static double adCCount = 0;
	private static double adNCCount = 0;
	private static double advertiserCCount = 0;
	private static double advertiserNCCount = 0;
	private static double titleCCount = 0;
	private static double titleNCCount = 0;
	private static double queryCCount = 0;
	private static double queryNCCount = 0;
	private static double desCCount = 0;
	private static double desNCCount = 0;
	private static double purchaseCCount = 0;
	private static double purchaseNCCount = 0;
	private static double userCCount = 0;
	private static double userNCCount = 0;
	private static double depthCCount = 0;
	private static double depthNCCount = 0;
	private static double positionCCount = 0;
	private static double positionNCCount = 0;
	
	private static double totalClk = 0;
	private static double totalImp = 0;
	private static IdnbFeature idFeatures;

	/**
	 * Main Function for training Call this to get a {@link IdFeatures}
	 * 
	 * @return IdFeatures
	 */
	public static IdnbFeature train() {
		idFeatures = new IdnbFeature();

		initial();
		// get the counts for each feature
		trainFeature(Config.trainingFilePath);

		// compute the probability for each feature;
		// Flag "T": title; "Q": query, "D": description, "P":purchaseKeyword,
		// "U": user
		computeProbability(idFeatures, "A");
		computeProbability(idFeatures, "Aer");
		computeProbability(idFeatures, "T");
		computeProbability(idFeatures, "Q");
		computeProbability(idFeatures, "D");
		computeProbability(idFeatures, "P");
		computeProbability(idFeatures, "U");
		computeProbability(idFeatures, "Prior");

		return idFeatures;
	}

	private static void initial() {
		titleC = new HashMap<Integer, Double>();
		titleNC = new HashMap<Integer, Double>();
		queryC = new HashMap<Integer, Double>();
		queryNC = new HashMap<Integer, Double>();
		descriptionC = new HashMap<Integer, Double>();
		descriptionNC = new HashMap<Integer, Double>();
		purchaseC = new HashMap<Integer, Double>();
		purchaseNC = new HashMap<Integer, Double>();
		adC = new HashMap<Integer, Double>();
		adNC = new HashMap<Integer, Double>();
		advertiserC = new HashMap<Integer, Double>();
		advertiserNC = new HashMap<Integer, Double>();
		userC = new HashMap<Integer, Double>();
		userNC = new HashMap<Integer, Double>();
		depthC = new HashMap<Integer, Double>();
		depthNC = new HashMap<Integer, Double>();
		positionC = new HashMap<Integer, Double>();
		positionNC = new HashMap<Integer, Double>();

	}

	private static void trainFeature(String trainingFile) {
		try {
			BufferedReader br = new BufferedReader(new FileReader(
					Config.trainingFilePath));
			String line;
			int lineNum = 0;
			System.out.println("IdNaiveBayes Training Begin.......");
			while ((line = br.readLine()) != null) {
				if (lineNum % 1000000 == 0)
					System.out.println(lineNum);
				lineNum++;
				String[] paras = line.split("\t");// id seg1|seg2|seg3....
				double click = Double.parseDouble(paras[0]);
				double impression = Double.parseDouble(paras[1]);
				int adID = Integer.parseInt(paras[3]);
				int advertiserID = Integer.parseInt(paras[4]);
				int depth = Integer.parseInt(paras[5]);
				int position = Integer.parseInt(paras[6]);
				int queryID = Integer.parseInt(paras[7]);
				int purchaseID = Integer.parseInt(paras[8]);
				int titleID = Integer.parseInt(paras[9]);
				int descriptionID = Integer.parseInt(paras[10]);
				int userID = Integer.parseInt(paras[11]);

				// update the training data for each feature.
				totalClk += click;
				totalImp += impression;

				// should handle not found situation

				update("A", adID, click, impression);
				update("Aer", advertiserID, click, impression);
				update("Q", queryID, click, impression);
				update("T", titleID, click, impression);
				update("P", purchaseID, click, impression);
				update("D", descriptionID, click, impression);
				update("U", userID, click, impression);
				//update("Dp",depth,click,impression);
				//update("Ps",position,click,impression);

			}
			br.close();
			System.out.println("ID Naive Bayes Training finished");
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	// update function: after get the value from hashmap in track2.data, we need
	// to update the hashmap of each training feature.
	private static void update(String flag, int value, double click,
			double impression) {
		double nClick = impression - click;
		if (flag.equals("Q")) {// Query
			updateMap(queryC, value, click);
			updateMap(queryNC, value, nClick);
			queryCCount += click;
			queryNCCount += nClick;
			//idFeatures.queryImpression[value] += impression;
		} else if (flag.equals("T")) {// Title
			updateMap(titleC, value, click);
			updateMap(titleNC, value, nClick);
			titleCCount += click;
			titleNCCount += nClick;
			//idFeatures.titleImpression[value] += impression;
		} else if (flag.equals("P")) {// Purchase
			updateMap(purchaseC, value, click);
			updateMap(purchaseNC, value, nClick);
			purchaseCCount += click;
			purchaseNCCount += nClick;
			//idFeatures.purchaseImpression[value] += impression;
		} else if (flag.equals("D")) {// Description
			updateMap(descriptionC, value, click);
			updateMap(descriptionNC, value, nClick);
			desCCount += click;
			desNCCount += nClick;
			//idFeatures.descriptionImpression[value] += impression;
		} else if (flag.equals("A")) {// Advertiser
			updateMap(adC, value, click);
			updateMap(adNC, value, nClick);
			adCCount += click;
			adNCCount += nClick;
			//idFeatures.adImpression[value] += impression;
		} else if (flag.equals("Aer")) {// AdvertiserId
			updateMap(advertiserC, value, click);
			updateMap(advertiserNC, value, nClick);
			advertiserCCount += click;
			advertiserNCCount += nClick;
			//idFeatures.advertiserImpression[value] += impression;
		} else if (flag.equals("U")) {// User
			updateMap(userC, value, click);
			updateMap(userNC, value, nClick);
			userCCount += click;
			userNCCount += nClick;
			//idFeatures.userImpression[value] += impression;
		}else if (flag.equals("Dp")) {// Depth
			updateMap(depthC, value, click);
			updateMap(depthNC, value, nClick);
			depthCCount += click;
			depthNCCount += nClick;
			//idFeatures.userImpression[value] += impression;
		}else if (flag.equals("Ps")) {// Position
			updateMap(positionC, value, click);
			updateMap(positionNC, value, nClick);
			positionCCount += click;
			positionNCCount += nClick;
			//idFeatures.userImpression[value] += impression;
		}

	}

	private static void updateMap(HashMap<Integer, Double> map, int key,
			double addTimes) {
		// System.out.println(key+"\t"+map.get(key));
		if (map.containsKey(key))
			map.put(key, map.get(key) + addTimes);
		else if (!map.containsKey(key)) {
			map.put(key, addTimes);
		}
		// System.out.println(key+"\t"+map.get(key));

	}

	private static void computeProbability(IdnbFeature bayes, String flag) {
		if (flag.equals("Q")) {
			// bayes.queryClick = new double[Config.queryIdSize];
			// bayes.queryNotClick = new double[Config.queryIdSize];
			// for (int i = 0; i < bayes.queryClick.length; i++) {
			// bayes.queryClick[i] = 1.0 / (queryCCount + Config.queryIdSize);
			// bayes.queryNotClick[i] = 1.0 / (queryNCCount +
			// Config.queryIdSize);
			// }
			//checkImpression(bayes.queryImpression, queryC, queryNC, "Q");
			compute(bayes.queryClick, queryC, queryCCount, Config.queryIdSize);
			compute(bayes.queryNotClick, queryNC, queryNCCount,
					Config.queryIdSize);
		} else if (flag.equals("T")) {
			// bayes.titleClick = new double[Config.titleIdSize];
			// bayes.titleNotClick = new double[Config.titleIdSize];
			// for (int i = 0; i < bayes.titleClick.length; i++) {
			// bayes.titleClick[i] = 1.0 / (titleCCount + Config.titleIdSize);
			// bayes.titleNotClick[i] = 1.0 / (titleNCCount +
			// Config.titleIdSize);
			// }
			//checkImpression(bayes.titleImpression, titleC, titleNC, "T");
			compute(bayes.titleClick, titleC, titleCCount, Config.titleIdSize);
			compute(bayes.titleNotClick, titleNC, titleNCCount,
					Config.titleIdSize);
		} else if (flag.equals("P")) {
			// bayes.purchaseClick = new double[Config.purchaseIdSize];
			// bayes.purchaseNotClick = new double[Config.purchaseIdSize];
			// for (int i = 0; i < bayes.purchaseClick.length; i++) {
			// bayes.purchaseClick[i] = 1.0 / (purchaseCCount +
			// Config.purchaseIdSize);
			// bayes.purchaseNotClick[i] = 1.0 / (purchaseNCCount +
			// Config.purchaseIdSize);
			// }
//			checkImpression(bayes.purchaseImpression, purchaseC, purchaseNC,
//					"P");
			compute(bayes.purchaseClick, purchaseC, purchaseCCount,
					Config.purchaseIdSize);
			compute(bayes.purchaseNotClick, purchaseNC, purchaseNCCount,
					Config.purchaseIdSize);
		} else if (flag.equals("D")) {
			// bayes.descriptionClick = new double[Config.descriptionIdSize];
			// bayes.descriptionNotClick = new double[Config.descriptionIdSize];
			// for (int i = 0; i < bayes.descriptionClick.length; i++) {
			// bayes.descriptionClick[i] = 1.0 / (desCCount +
			// Config.descriptionIdSize);
			// bayes.descriptionNotClick[i] = 1.0 / (desNCCount +
			// Config.descriptionIdSize);
			// }
//			checkImpression(bayes.descriptionImpression, descriptionC,
//					descriptionNC, "D");
			compute(bayes.descriptionClick, descriptionC, desCCount,
					Config.descriptionIdSize);
			compute(bayes.descriptionNotClick, descriptionNC, desNCCount,
					Config.descriptionIdSize);
		} else if (flag.equals("U")) {// UserID
			// bayes.userClick = new double[Config.userIdSize];
			// bayes.userNotClick = new double[Config.userIdSize];
			// for (int i = 0; i < bayes.userClick.length; i++) {
			// bayes.userClick[i] = 1.0 / (userCCount + Config.userIdSize);
			// bayes.userNotClick[i] = 1.0 / (userNCCount + Config.userIdSize);
			// }
			//checkImpression(bayes.userImpression, userC, userNC, "U");
			compute(bayes.userClick, userC, userCCount, Config.userIdSize);
			compute(bayes.userNotClick, userNC, userNCCount, Config.userIdSize);

		} else if (flag.equals("A")) {// AdId
			// bayes.adClick = new double[Config.adIdSize];
			// bayes.adNotClick = new double[Config.adIdSize];
			// for (int i = 0; i < bayes.adClick.length; i++) {
			// bayes.adClick[i] = 1.0 / (adCCount + Config.adIdSize);
			// bayes.adNotClick[i] = 1.0 / (adNCCount + Config.adIdSize);
			// }
			//checkImpression(bayes.adImpression, adC, adNC, "A");
			compute(bayes.adClick, adC, adCCount, Config.adIdSize);
			compute(bayes.adNotClick, adNC, adNCCount, Config.adIdSize);

		} else if (flag.equals("Aer")) {// AdvertiserID
			// bayes.advertiserClick = new double[Config.advertiserIdSize];
			// bayes.advertiserNotClick = new double[Config.advertiserIdSize];
			// for (int i = 0; i < bayes.advertiserClick.length; i++) {
			// bayes.advertiserClick[i] = 1.0 / (advertiserCCount +
			// Config.advertiserIdSize);
			// bayes.advertiserNotClick[i] = 1.0 / (advertiserNCCount +
			// Config.advertiserIdSize);
			// }
			// checkImpression(bayes.advertiserImpression, advertiserC,
			// advertiserNC, "Aer");
			compute(bayes.advertiserClick, advertiserC, advertiserCCount,
					Config.advertiserIdSize);
			compute(bayes.advertiserNotClick, advertiserNC, advertiserNCCount,
					Config.advertiserIdSize);

		}else if (flag.equals("Dp")) {// depth
			
			compute(bayes.depthClick, depthC, depthCCount,
					3);
			compute(bayes.depthNotClick, depthNC, depthNCCount,
					3);

		}else if (flag.equals("Ps")) {// position
			
			compute(bayes.positionClick, positionC, positionCCount,
				3);
			compute(bayes.positionNotClick, positionNC, positionNCCount,
					3);

		} else if (flag.equals("Prior")) {
			bayes.click = (totalClk + 1.0) / (totalImp + 2.0);
		}
	}

	private static void checkImpression(double[] queryImpression,
			HashMap<Integer, Double> click, HashMap<Integer, Double> nClick,
			String flag) {
		// TODO Auto-generated method stub

		for (int i = 1; i < queryImpression.length; i++) {
			if (queryImpression[i] == 0.0) {
				continue;
			}
			 //System.out.println(flag + "\t" + i + "\t" + queryImpression[i]);
			if (queryImpression[i] <= Config.times) {
				if (click.get(i) == 0) {
					if (nClick.containsKey(i)) {
						nClick.remove(i);
						if (flag.equals("Q")) {
							queryNCCount -= queryImpression[i];
						} else if (flag.equals("T")) {
							titleNCCount -= queryImpression[i];
						} else if (flag.equals("P")) {
							purchaseNCCount -= queryImpression[i];
						} else if (flag.equals("D")) {
							desNCCount -= queryImpression[i];
						} else if (flag.equals("A")) {
							adNCCount -= queryImpression[i];
						} else if (flag.equals("Aer")) {
							advertiserNCCount -= queryImpression[i];
						} else if (flag.equals("U")) {
							userNCCount -= queryImpression[i];
						}
					}
				}
			}
		}
	}

	// compute probability using formula and store in NaiveBayesFeatures
	private static void compute(double[] prob, HashMap<Integer, Double> mapC,
			double count, double size) {
		java.util.Iterator<Entry<Integer, Double>> iter = mapC.entrySet()
				.iterator();
		while (iter.hasNext()) {
			Entry<Integer, Double> entry = iter.next();
			int key = entry.getKey();
			double value = entry.getValue();
			double probability = (value + 1) / (count + mapC.size() * 1.0);
			prob[key] = probability;
		}

	}

}
